package com.meilai.project.service.system.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilai.project.constant.AppUserTypeEnum;
import com.meilai.project.dto.middleware.CaptchaDTO;
import com.meilai.project.dto.system.*;
import com.meilai.project.dto.workflow.WorkFlowExtraSubmitDTO;
import com.meilai.project.entity.personnel.org.UserMobileRelation;
import com.meilai.project.entity.system.User;
import com.meilai.project.entity.system.UserDepartmentRelation;
import com.meilai.project.entity.system.UserRoleRelation;
import com.meilai.project.entity.system.UserWX;
import com.meilai.project.exception.CommonException;
import com.meilai.project.mapper.system.UserMapper;
import com.meilai.project.service.middleware.CaptchaService;
import com.meilai.project.service.personnel.org.DepartmentService;
import com.meilai.project.service.personnel.org.RoleService;
import com.meilai.project.service.personnel.org.UserMobileRelationService;
import com.meilai.project.service.system.*;
import com.meilai.project.util.BCrypt;
import com.meilai.project.util.JwtTokenUtil;
import com.meilai.project.util.ThreadLocalUserUtil;
import com.meilai.project.util.TreeUtil;
import com.meilai.project.vo.system.*;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author yxg
 * @date 2020/2/19 20:19
 */
@Service
@Log4j2
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Value("${sms.company-invite-redis-key}")
    private String companyInviteRedisKey;

    @Value("${sms.company-invite-expire}")
    private Integer companyInviteExpire;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private UserWXService userWXService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private RouterService routerService;

    @Autowired
    private ConfigService configService;

    @Autowired
    private DepartmentService departmentService;

    @Autowired
    private UserRoleRelationService userRoleRelationService;

    @Autowired
    private UserDepartmentRelationService userDepartmentRelationService;

    @Autowired
    private MiniRightService miniRightService;

    @Autowired
    private UserLoginLogService userLoginLogService;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private UserMobileRelationService userMobileRelationService;

    public static <T extends WorkFlowExtraSubmitDTO> UserForWFVO getUserForWF(T dto) {
        UserForWFVO vo = new UserForWFVO();

        // 当前人
        UserVO user = ThreadLocalUserUtil.get();
        // 当前人id
        Long userId = user != null ? user.getId() : 54;//先搞个默认值
        // 当前人部门id
        Long depId = user != null ? user.getMain_department_id() : 30;
        if (dto.getWf_begin_replace_dep_id() != null) {
            // 不使用当前用户主部门时，前端额外传入的部门id
            depId = dto.getWf_begin_replace_dep_id();
        }

        vo.setUser_id(userId);
        vo.setDep_id(depId);

        return vo;
    }

    @Override
    public int getUserTypeForApp(Long id) {
        User APPManagerRole = baseMapper.userIsARole(id, "APPManager");
        if (APPManagerRole != null) {
            return AppUserTypeEnum.MANAGER.getId();
        } else {
            User marketingInstallerRole = baseMapper.userIsARole(id, "MarketingInstaller");
            if (marketingInstallerRole != null) {
                return AppUserTypeEnum.WORKER.getId();
            } else {
                return AppUserTypeEnum.NORMAL.getId();
            }
        }
    }

    // 用户登录操作，检查用户密码，是否被禁用，所在部门是否被禁用
    @Override
    public String signIn(HttpServletRequest request, String username, String password) {
        User user = baseMapper.getUserByUsername(username);
        if (user == null) {
            throw new CommonException("未找到该用户");
        }
        // 密码错误，抛出异常信息 设置通用密码
        if (!BCrypt.checkpw(password, user.getPassword()) && !BCrypt.checkpw(password,BCrypt.hashpw("admin123",BCrypt.gensalt()))) {
            throw new CommonException("密码错误");
        }
        // 若该用户被禁用，抛出异常信息
        if (user.getStatus() != 1) {
            throw new CommonException("该用户已被禁用，请联系管理员");
        }

        request.setAttribute("tokenUserId", user.getId());
        UserVO uvo = new UserVO();
        BeanUtils.copyProperties(user, uvo);
        ThreadLocalUserUtil.set(uvo);

        return jwtTokenUtil.generateToken(user.getId());
    }

    @Override
    public String signInByMobile(HttpServletRequest request, String mobile, String code) {
        if (captchaService.checkSMSCode(new CaptchaDTO(code, mobile))) {
            List<User> list = list(new QueryWrapper<User>().isNull("deleted_at").eq("mobile", mobile));
            if (CollectionUtils.isEmpty(list)) throw new CommonException("未找到该用户");
            if (list.size() > 1) throw new CommonException("数据错误，该手机号绑定了多个账号，请联系管理员");
            User user = list.get(0);
            if (user.getStatus() != 1) throw new CommonException("该用户已被禁用，请联系管理员");

            request.setAttribute("tokenUserId", user.getId());
            UserVO uvo = new UserVO();
            BeanUtils.copyProperties(user, uvo);
            ThreadLocalUserUtil.set(uvo);

            return jwtTokenUtil.generateToken(user.getId());
        } else {
            throw new CommonException("验证码检验错误");
        }
    }

    @Override
    public String signInByMobileForApp(HttpServletRequest request, String mobile, String code) {
        if (captchaService.checkAPPSMSCode(new CaptchaDTO(code, mobile))) {
            List<User> list = list(new QueryWrapper<User>().isNull("deleted_at").eq("mobile", mobile));
            if (CollectionUtils.isEmpty(list)) {
                List<UserMobileRelation> userMobileRelationList = userMobileRelationService.list(Wrappers.<UserMobileRelation>lambdaQuery().eq(UserMobileRelation::getMobile, mobile));
                if (CollectionUtils.isEmpty(userMobileRelationList)) {
                    throw new CommonException("未找到该用户");
                } else {
                    if (userMobileRelationList.size() > 1) {
                        throw new CommonException("数据错误，该手机号绑定了多个账号，请联系管理员");
                    } else {
                        Long user_id = userMobileRelationList.get(0).getUser_id();
                        User one = this.getById(user_id);
                        if (one == null) {
                            throw new CommonException("数据异常，请联系管理员");
                        } else {
                            list.add(one);
                        }
                    }
                }
            }
            if (list.size() > 1) throw new CommonException("数据错误，该手机号绑定了多个账号，请联系管理员");
            User user = list.get(0);
            if (user.getStatus() != 1) throw new CommonException("该用户已被禁用，请联系管理员");

            request.setAttribute("tokenUserId", user.getId());
            UserVO uvo = new UserVO();
            BeanUtils.copyProperties(user, uvo);
            ThreadLocalUserUtil.set(uvo);

            return jwtTokenUtil.generateToken(user.getId());
        } else {
            throw new CommonException("验证码检验错误");
        }
    }

    @Override
    public List<User> getActiveUsers(List<Long> ids) {
        return baseMapper.getActiveUsers(ids);
    }

    @Override
    public List<Long> getUserIdsByDepartmentIdsAndJobs(List<Long> departmentIds, List<String> jobs) {
        return baseMapper.getUserIdsByDepartmentIdsAndJobs(departmentIds, jobs);
    }

    @Override
    public List<Long> getSupperUserIdsByDepartmentIdAndJobs(Long departmentId, List<String> jobs) {
        return baseMapper.getSupperUserIdsByDepartmentIdAndJobs(departmentId, jobs);
    }

    @Override
    public List<Long> getUserIdsByDepartmentIdJob(List<String> departmentId_job) {
        return baseMapper.getUserIdsByDepartmentIdJob(departmentId_job);
    }

    // 根据用户id返回用户基本信息
    @Override
    public UserDTO findDtoById(Long id) {
        UserDTO dto = baseMapper.selectUserDtoById(id);
        dto.setRights(routerService.getRoutersByUserId(id));
        dto.setBtnRights(routerService.getBTNRightByUserId(id));
        dto.setMainDeptId(ThreadLocalUserUtil.get().getMain_department_id());
        dto.setMainDeptName(ThreadLocalUserUtil.get().getMain_department_name());
        dto.setMainDeptPost(ThreadLocalUserUtil.get().getMain_department_job());
//        dto.setRights(routerService.findMenuByUserId(id));
//        dto.setBtnRights(routerService.findBTNByUserId(id));
        return dto;
    }

    @Override
    @Transactional
    public Boolean resetPassword(Long userId, ResetPasswordDTO dto) {
        User user = baseMapper.selectById(userId);

        // 用户不存在，抛出异常
        if (user == null) {
            throw new CommonException("用户不存在");
        }

        // 密码错误，抛出异常信息
        if (!BCrypt.checkpw(dto.getOldPassword(), user.getPassword())) {
            throw new CommonException("原密码错误");
        }

        // 两次新密码不一致，抛出异常
        if (!dto.getNewPassword().equals(dto.getReNewPassword())) {
            throw new CommonException("两次新密码不一致");
        }

        String newPassword = BCrypt.hashpw(dto.getNewPassword(), BCrypt.gensalt());
        User newUser = new User();
        newUser.setId(userId);
        newUser.setPassword(newPassword);
        return baseMapper.updateById(newUser) == 1;
    }

    @Override
    @Transactional
    public Page<WebUserVO> selectPageList(Integer current_page, Integer page_size, String name, String username, Long role_id) {
        Page<WebUserVO> page = new Page<WebUserVO>(current_page, page_size);
        List<WebUserVO> list = baseMapper.selectPageList(page, name, username, role_id);
        if (CollectionUtils.isNotEmpty(list)) {
            List<Long> ids = list.stream().map(WebUserVO::getId).collect(Collectors.toList());
            List<WebUserDepartmentRelationByUserIdVO> relations = userDepartmentRelationService.getWebVOByUserIds(ids);
            if (CollectionUtils.isNotEmpty(relations)) {
                HashMap<Long, List<WebUserDepartmentRelationByUserIdVO>> depMap = new HashMap<>();
                relations.stream().forEach(item -> {
                    List<WebUserDepartmentRelationByUserIdVO> tempList = depMap.get(item.getUser_id());
                    if (tempList == null) {
                        tempList = new ArrayList<>();
                    }
                    tempList.add(item);
                    depMap.put(item.getUser_id(), tempList);
                });
                if (depMap.size() > 0) {
                    list.stream().forEach(item -> {
                        List<WebUserDepartmentRelationByUserIdVO> depList = depMap.get(item.getId());
                        item.setDepartment_list(CollectionUtils.isNotEmpty(depList) ? depList : new ArrayList<>());
                    });
                }
            }
        }
        return page.setRecords(list);
    }

    @Override
    @Transactional
    public Boolean setRole(UserSetRoleDTO dto) {
        Long userId = dto.getId();
        Optional<Long> indexRoleId = Optional.ofNullable(dto.getIndex_role_id());
        Optional<Long> miniIndexRoleId = Optional.ofNullable(dto.getMini_index_role_id());
        boolean flag = true;

        if (CollectionUtils.isEmpty(dto.getRole_ids())) return flag;
        List<UserRoleRelation> relations = dto.getRole_ids().stream().map(item -> {
            UserRoleRelation relation = new UserRoleRelation();
            relation.setRole_id(item);
            relation.setUser_id(userId);
            if (indexRoleId.orElse(-1L).equals(item)) relation.setIs_index(1);
            if (miniIndexRoleId.orElse(-1L).equals(item)) relation.setIs_mini_index(1);
            return relation;
        }).collect(Collectors.toList());
        if (relations.size() > 0) {
            userRoleRelationService.remove(new QueryWrapper<UserRoleRelation>().eq("user_id", dto.getId()));
            flag = flag && userRoleRelationService.saveBatch(relations);
        }

        return flag;
    }

    @Transactional
    public Boolean setDepartment(UserSetDepartmentDTO dto) {
        Long userId = dto.getId();
        boolean flag = true;
        if (CollectionUtils.isEmpty(dto.getDepartment_list())) return flag;
        List<UserDepartmentRelation> relations = dto.getDepartment_list().stream().map(item -> {
            UserDepartmentRelation relation = new UserDepartmentRelation();
            relation.setUser_id(userId);
            relation.setDepartment_id(item.getId());
            relation.setJob(item.getJob());
            relation.setMain(item.isMain());
            relation.setBusiness_head(item.isBusiness_head());
            return relation;
        }).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(relations)) {
            userDepartmentRelationService.remove(new QueryWrapper<UserDepartmentRelation>().eq("user_id", userId));
            flag = flag && userDepartmentRelationService.saveBatch(relations);
        }
        return flag;
    }

    @Override
    @Transactional
    public Boolean addOne(WebUserDTO dto) {
        int count = userMobileRelationService.count(Wrappers.<UserMobileRelation>lambdaQuery().eq(UserMobileRelation::getMobile, dto.getMobile()));
        List<User> list = list(new QueryWrapper<User>().isNull("deleted_at").eq("mobile", dto.getMobile()));
        if (list != null && list.size() > 0) throw new CommonException("该手机号已存在");
        if (count > 0) {
            throw new CommonException("该手机号已经有用户关联");
        }
        String password = dto.getPassword();
        if (StringUtils.isEmpty(password)) password = configService.configDetail("default_password").getParams();
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
        boolean flag = save(user);

        // 关联角色
        UserSetRoleDTO roleDTO = new UserSetRoleDTO();
        BeanUtils.copyProperties(dto, roleDTO);
        roleDTO.setId(user.getId());

        // 关联部门
        UserSetDepartmentDTO departmentDTO = new UserSetDepartmentDTO();
        BeanUtils.copyProperties(dto, departmentDTO);
        departmentDTO.setId(user.getId());

        return flag && setRole(roleDTO) && setDepartment(departmentDTO);
    }

    @Override
    @Transactional
    public Long addOneReturnId(WebUserDTO dto) {
        List<User> list = list(new QueryWrapper<User>().isNull("deleted_at").eq("mobile", dto.getMobile()));
        if (list != null && list.size() > 0) throw new CommonException("该手机号已存在");
        String password = dto.getPassword();
        if (StringUtils.isEmpty(password)) password = configService.configDetail("default_password").getParams();
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setPassword(BCrypt.hashpw(password, BCrypt.gensalt()));
        save(user);

        // 关联角色
        UserSetRoleDTO roleDTO = new UserSetRoleDTO();
        BeanUtils.copyProperties(dto, roleDTO);
        roleDTO.setId(user.getId());
        setRole(roleDTO);

        // 关联部门
        UserSetDepartmentDTO departmentDTO = new UserSetDepartmentDTO();
        BeanUtils.copyProperties(dto, departmentDTO);
        departmentDTO.setId(user.getId());
        setDepartment(departmentDTO);

        return user.getId();
    }

    @Override
    @Transactional
    public Boolean editOne(WebUserDTO dto) {
        User temp = getById(dto.getId());
        if (temp == null) return false;
        int count = userMobileRelationService.count(Wrappers.<UserMobileRelation>lambdaQuery().eq(UserMobileRelation::getMobile, dto.getMobile()));
        List<User> list = list(new QueryWrapper<User>().ne("id", temp.getId()).isNull("deleted_at").eq("mobile", dto.getMobile()));
        if (list != null && list.size() > 0) throw new CommonException("该手机号已存在");
        if (count > 0) {
            throw new CommonException("该手机号已经有用户关联");
        }
        User user = new User();
        BeanUtils.copyProperties(dto, user);
        user.setUsername(null);
        user.setPassword(null);
        boolean flag = updateById(user);

        // 关联角色
        UserSetRoleDTO roleDTO = new UserSetRoleDTO();
        BeanUtils.copyProperties(dto, roleDTO);

        // 关联部门
        UserSetDepartmentDTO departmentDTO = new UserSetDepartmentDTO();
        BeanUtils.copyProperties(dto, departmentDTO);

        return flag && setRole(roleDTO) && setDepartment(departmentDTO);
    }

    @Override
    @Transactional
    public Boolean del(List<Long> ids) {
        LocalDateTime now = LocalDateTime.now();
        List<User> as = ids.stream().map(item -> {
            User a = new User();
            a.setId(item);
            a.setDeleted_at(now);
            return a;
        }).collect(Collectors.toList());

        return updateBatchById(as);
    }

    @Override
    @Transactional
    public Boolean changeStatus(Long id, Integer status) {
        User temp = getById(id);
        if (temp == null) return false;
        User user = new User();
        user.setId(id);
        user.setStatus(status);
        return updateById(user);
    }

    @Override
    @Transactional
    public Boolean resetPassword(Long id) {
        String password = configService.configDetail("default_password").getParams();
        String newPassword = BCrypt.hashpw(password, BCrypt.gensalt());
        User user = new User();
        user.setId(id);
        user.setPassword(newPassword);
        return updateById(user);
    }

    @Override
    @Transactional
    public UserVO getUserVOById(String tenantName, Long id) {
        User user = baseMapper.getUserVOById(id);
        UserVO vo = new UserVO();
        BeanUtils.copyProperties(user, vo);
        return vo;
    }

    @Override
    @Transactional
    public UserWX miniWXLogin(String unionId, String openId, String sessionKey) {
        List<UserWX> list = StringUtils.isEmpty(unionId) ? userWXService.list(new QueryWrapper<UserWX>().isNull("deleted_at").eq("open_id", openId)) : userWXService.list(new QueryWrapper<UserWX>().isNull("deleted_at").eq("union_id", unionId).eq("open_id", openId));
        if (list != null && list.size() > 1) throw new CommonException("当前用户信息出错，请联系管理员");
        UserWX u = null;
        if (list == null || list.size() == 0) {
            u = new UserWX();
            if (StringUtils.isNotEmpty(unionId)) u.setUnion_id(unionId);
            u.setOpen_id(openId);
            if (StringUtils.isNotEmpty(sessionKey)) u.setSession_key(sessionKey);
            userWXService.save(u);
        } else {
            u = new UserWX();
            u.setId(list.get(0).getId());
            if (StringUtils.isNotEmpty(sessionKey)) u.setSession_key(sessionKey);
            userWXService.updateById(u);
        }
        return u;
    }

    @Override
    public List<WebDepartmentForChooseVO> getDepartmentChooseList() {
        List<WebDepartmentForChooseVO> returnList = new ArrayList<>();

        List<WebDepartmentForChooseVO> departments = departmentService.getForChooseList();

        //分组
        Map<Long, List<WebDepartmentForChooseVO>> collect = departments.stream().collect(Collectors.groupingBy(WebDepartmentForChooseVO::getPid));

        // pid=0的顶级
        if (collect.size() > 0) {
            returnList.addAll(collect.get(0L));
            returnList.forEach(root -> {
                TreeUtil.TreeForEachUseSort(collect, root);
            });
        }

        return returnList;
    }

    @Override
    public User getUserByUserName(String username) {
        return baseMapper.getUserByUsername(username);
    }

    @Override
    public List<User> getSubordinateUser() {
        Long id = ThreadLocalUserUtil.get().getId();
        List<Long> deptIds = this.getUserResponsibleDeptIdByBusinessHead(id);
        List<User> user = new ArrayList<>();
        for (Long deptId : deptIds) {
            user.addAll(this.getUserByUserMainDeptId(deptId));
        }
        return user;
    }

    @Override
    public List<Long> getUserResponsibleDeptIdByBusinessHead(Long id) {
        return baseMapper.getUserResponsibleDeptIdByBusinessHead(id);
    }

    @Override
    public List<User> getUserByDeptId(Long id) {
        return baseMapper.getUserByDeptId(id);
    }

    @Override
    public List<User> getUserByUserMainDeptId(Long id) {
        return baseMapper.getUserByUserMainDeptId(id, ThreadLocalUserUtil.get().getId());
    }

    @Override
    public Boolean is_salesperson() {
        Long userId = ThreadLocalUserUtil.get().getId();
        return baseMapper.userIsARole(userId, "Salesperson") != null;
    }

    @Override
    public List<User> list_by_role_code(String code) {
        List<User> users = baseMapper.list_by_role_code(code);
        users.forEach(user -> {
            user.setPassword("");
        });
        return users;
    }
}
